
/**
 * Universidad del Valle de Guatemala
 * CC2003 Algoritmos y Estructuras de Datos
 * Proyecto 1: Intérprete de LISP --> Lambda LISP
 * @version 2 Septiembre, 2009
 * Integrantes:
 *      @author Julio Chicas, José García, Ernesto Rodríguez, Karen Tojín
 **/
/**
 * Descripción: Clase auxiliar para definir las funciones mediante la instrucción DEFUN
 **/
import java.util.List;

public class Function {

    private Lista<String> fun = new Lista<String>();
    private List<String> lis;
    private String nom;

    /**
     * Método get para devolver una lista con la función ingresada
     * @return Una lista de String
     */
    public Lista<String> getFun() {
        return fun;
    }

    /**
     * Método "set" para las funciones que se definen
     * @param name: Nombre de la función
     * @param operands: Operandos de la función
     * @param function: Lo que va a realizar la función
     */
    public Function(String name, List operands, List<String> function) {

        fun = (Lista) function;
        nom = name;
        lis = operands;
    }

    /**
     * Método get para devolver el nombre de la función
     * @return El nombre de la función
     */
    public String getName() {
        return nom;
    }

    /**
     * Método get para devolver los operandos de la función
     * @return Una lista de String con los operandos de la función
     */
    public List<String> getOperands() {
        return lis;
    }

    /**
     * Método que define la función ingresada
     * @param operands: Los operandos de la función
     * @param function: Lo que la función va a realizar
     */
    public void set(List operands, List<String> function) {
        lis = operands;
        fun = (Lista) function;
    }

    /**
     * Método que devuelve la cantidad de operandos de la función
     * @return Un entero con la cantidad de operandos de la función
     */
    public int size() {
        return lis.size();
    }

    /**
     * Método de identifica si el nombre ingresado es el de una función definida
     * @param name: El nombre ingresado de la función
     * @return Si el nombre ingresado de la función es igual al definido
     */
    public Boolean eq(String name) {
        return name.equals(nom);
    }

    /**
     * Método para definir los operandos de la función a VarArray
     * @param localVar: A donde se pasarán los operandos
     * @param operands: Los operandos definidos
     * @return var que contiene los operandos redefinidos
     * @throws UnoperableException
     */
    private VarArray redefineVariables(VarArray localVar, List operands) throws UnoperableException {
        VarArray var = new VarArray();
        Variable toAdd = null;
        for (int a = 0; a < operands.size(); a++) {
            toAdd = new Variable(lis.get(a), Operations.operate(operands.get(a), localVar));
            toAdd.unset();
            var.add(toAdd);

        }
        for (int a = 0; a < localVar.size(); a++) {
            var.addExclusive(localVar.get(a));
        }
        return var;
    }

    /**
     * Método que evalua las funciones definidas
     * @param oper: los operandos
     * @param localVar: A donde se van a redefinir los operandos
     * @return El resultado de la función
     * @throws UnoperableException
     */
    public Object evalFun(List oper, VarArray localVar) throws UnoperableException {
        if (oper.size() == size()) {

            Object res = null;
            VarArray variables = redefineVariables(localVar, oper);
            for (int a = 0; a < fun.size(); a++) {
                res = Operations.operate(fun.get(a), variables);
            }
            return res;


        }
        throw new UnoperableFunctionException();
    }
}
